////////////////////////////////////////////////////////
// CodePlex - metacpp
//--------------------
// This file is part of "metacpp" project on CodePlex. The
// goal if the project is to demonstrate basic techniques
// used in C++ metaprogrammins.
// For more info and comments please visit:
// http://www.codeplex.com/metacpp/
////////////////////////////////////////////////////////

#include <iostream>
#include <string>

#include "typelists.hpp"
#include "variant.hpp"

using namespace metacpp;


/// This class is used to test "apply_for-each" construct
template<typename T>
struct TypeWrapper {
	T _t;
	inline TypeWrapper(const T& t) : _t(t) {}
	friend std::ostream & operator<<(std::ostream &out, const TypeWrapper<T> &tw) {
		return (out << "Generic TypeWrapper: " << tw._t);
	}
};
template<>
struct TypeWrapper<int> {
	int _t;
	inline TypeWrapper(const int& t) : _t(t) {}
	friend std::ostream & operator<<(std::ostream &out, const TypeWrapper<int> &tw) {
		return (out << "Int TypeWrapper: " << tw._t);
	}
};
void testApplyToEach(void) {
	typedef TYPELIST_2(char, int) dlist;
	typedef apply_for_each<dlist, TypeWrapper>::type wraplist;

	tlist_get_type<wraplist, 0>::type w1('a');
	tlist_get_type<wraplist, 1>::type w2(10);
	std::cout << "Apply for each: " << w1 << ", " << w2 << std::endl;
}


///////////////////////////////////////////////////
///////////////////////////////////////////////////


void testAbstractVisitor(void) {
	typedef TYPELIST_3(long, char, std::string) tlist;
	typedef AbstractVisitor<tlist> visitor_base;

/*	class nick : public AbstractVisitor<TYPELIST_2(char, std::string)> {
	public:
		virtual void operator()(const char&) {}
		virtual void operator()(const std::string&) {}
	};*/
	// implements only part of required interfaces
	class partial : public visitor_base {
	public:
		virtual void operator()(const long&) {}
		virtual void operator()(const std::string&) {}
	};

	// implements remaining interfaces
	class complete : public partial {
		virtual void operator()(const char&) {}
	};

	/*THIS LINE SHOULD NOT COMPILE*/ // partial p;
	complete c;
	//nick* pnick;
	//pnick = &c;
}


///////////////////////////////////////////////////
///////////////////////////////////////////////////


void test_typelists(void) {
	std::cout << "=== Testing TYPELISTS ===" << std::endl;

	typedef TYPELIST_5(char, int, std::string, double, unsigned long) tlist;

	std::cout << "Number of types in list: "
		<< tlist_length<tlist>::value << std::endl;

	std::cout << "The sizes of 1st and 4th types are: "
		<< sizeof(tlist_get_type<tlist, 0>::type) << ", " 
		<< sizeof(tlist_get_type<tlist, 3>::type) << std::endl;
	/*THIS LINE SHOULD NOT COMPILE*/ // typedef tlist_get_type<tlist, 5>::type overflow;

	std::cout << "The size of largest type is: "
		<< sizeof(largest_type<tlist>::type) << std::endl;

	std::cout << "Index of \"std::string\" is: "
		<< index_of_type<tlist, std::string>::value << ", " 
		<< "and of \"ulong\" is: " << index_of_type<tlist, unsigned long>::value << std::endl;
	/*THIS LINE SHOULD NOT COMPILE*/ //int notfound = index_of_type<tlist, float>::value;

	testApplyToEach();
	testAbstractVisitor();
}

class Integer {
	int _i;
public:
	inline Integer(int i) : _i(i) { std::cout << "Integer ctor" << std::endl; }
	inline Integer(const Integer &i) : _i(i._i) { std::cout << "Integer copy ctor" << std::endl; }
	~Integer(void) { std::cout << "Integer dtor" << std::endl; }

	friend std::ostream & operator<<(std::ostream &out, const Integer &i) {
		return (out << i._i);
	}
};


void test_variant(void) {
	std::cout << "=== Testing VARIANT ===" << std::endl;
	
	typedef TYPELIST_4(int, std::string, double, Integer) var_tlist;
	typedef variant<var_tlist> my_variant;
	std::cout << "Size of largest type is: "
		<< sizeof(largest_type<var_tlist>::type) 
		<< ", size of variant is: " << sizeof(my_variant) << std::endl;

	my_variant v0;
	my_variant v1(10);
	my_variant v2(std::string("hello"));
	my_variant v3(Integer(5));
	my_variant v4(v3);
	my_variant v5 = 11;

	std::cout << "Initial values: v0="<<v0 << ", v1="<< v1
		<< ", v2="<< v2 << ", v3="<<v3 << ", v4="<<v4 << std::endl;

	v4 = v2;
	v2 = 15;

	std::cout << "Values after assingment: v0="<<v0 << ", v1="<< v1
		<< ", v2="<< v2 << ", v3="<<v3 << ", v4="<<v4 << std::endl;

	int i = v1.get<int>();
	std::cout << "Retrieved <int> from v1: " << i << std::endl;

	try {
		double d = v1.get<double>();
	} catch (std::bad_cast &e) {
		std::cout << "Failed to get <double> from v1 - bad_cast exception: " << e.what() << std::endl;
	}

}



int main(int argc, char *argv[]) {
	test_typelists();
	std::cout << std::endl;

	test_variant();
	std::cout << std::endl;

	return 0;
}